package edu.hawaii.ics.pjdb.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import edu.hawaii.ics.pjdb.types.Command;
import edu.hawaii.ics.pjdb.types.CsvPartitionCommand;
import edu.hawaii.ics.pjdb.types.DbSourceCommand;
import edu.hawaii.ics.pjdb.types.DbSyncRowSetCommand;
import edu.hawaii.ics.pjdb.types.Plan;
import edu.hawaii.ics.pjdb.types.RemoteAddress;
import edu.hawaii.ics.pjdb.types.RunScriptCommand;
import edu.hawaii.ics.pjdb.types.WorkerCommand;
import edu.hawaii.ics.pjdb.types.WorkerInformation;

/**
 * Provides a simpler interface to build Plans for the PJDB system.
 * 
 * @author Christopher Foo
 * 
 */
public class PlanBuilder {

    /**
     * Creates a CsvPartition command plan that reads a CSV file, partitions the
     * data, and dumps the tuples in the specified databases.
     * 
     * @param worker
     *            The information of the worker to run the CsvPartition command.
     * @param csvFile
     *            The CSV file to be read.
     * @param header
     *            If the CSV file has a header that needs to be skipped.
     * @param workers
     *            The information of the other workers that will handle the
     *            partitioned data.
     * @param partitionFunction
     *            The name of the function used to partition the data.
     * @param partitionFieldIndex
     *            The index of the field to partition on.
     * @param arguments
     *            The arguments needed by the partitioning function.
     * @return A Plan that describes the actions that must be taken to do the
     *         specified action.
     */
    public static Plan createCsvPartitionPlan(WorkerInformation worker,
            String csvFile, boolean header, WorkerInformation[] workers,
            String partitionFunction, int partitionFieldIndex,
            List<String> arguments) {
        Plan plan = new Plan();
        List<WorkerCommand> commands = new ArrayList<WorkerCommand>();
        List<RemoteAddress> outputAddresses = new ArrayList<RemoteAddress>();

        // Set up DbSync commands
        for (WorkerInformation info : workers) {
            commands.add(new WorkerCommand()
                    .setWorkerAddress(info.getWorkerAddress())
                    .setDbPath(info.getDbPath())
                    .setCommand(
                            Command.dbSyncRowSetCmd(new DbSyncRowSetCommand()
                                    .setInputAddress(info.getInputAddress())
                                    .setTableName(info.getTableName()))));
            outputAddresses.add(info.getInputAddress());
        }

        // Set up CsvPartition command
        commands.add(new WorkerCommand().setWorkerAddress(
                worker.getWorkerAddress()).setCommand(
                Command.csvPartitionCmd(new CsvPartitionCommand()
                        .setCsvFile(csvFile).setHeader(header)
                        .setOutputAddresses(outputAddresses)
                        .setPartitionFunctionName(partitionFunction)
                        .setPartitionFieldIndex(partitionFieldIndex)
                        .setArguments(arguments))));
        plan.setCommands(commands);
        return plan;
    }

    /**
     * Creates a DbSource command plan to run the given SQL string on the given
     * worker.
     * 
     * @param worker
     *            The worker to run the DbSource command.
     * @param sql
     *            The SQL command to be run.
     * @return The Plan that runs the DbSource commmand.
     */
    public static Plan createDbSourcePlan(WorkerInformation worker, String sql) {
        Plan plan = new Plan();
        List<WorkerCommand> commands = Arrays
                .asList(new WorkerCommand()
                        .setWorkerAddress(worker.getWorkerAddress())
                        .setDbPath(worker.getDbPath())
                        .setOutputAddress(worker.getOutputAddress())
                        .setCommand(
                                Command.dbSourceCmd(new DbSourceCommand()
                                        .setSql(sql))));
        plan.setCommands(commands);
        return plan;
    }

    /**
     * Creates a Plan to run a script.
     * 
     * @param workers
     *            The workers to run the script on.
     * @param script
     *            The script to run on the workers.
     * @return The Plan that runs the script.
     */
    public static Plan createScriptPlan(WorkerInformation[] workers,
            String script) {
        Plan plan = new Plan();
        List<WorkerCommand> commands = new ArrayList<WorkerCommand>();

        for (WorkerInformation worker : workers) {
            commands.add(new WorkerCommand()
                    .setWorkerAddress(worker.getWorkerAddress())
                    .setDbPath(worker.getDbPath())
                    .setOutputAddress(worker.getOutputAddress())
                    .setCommand(
                            Command.runScriptCmd(new RunScriptCommand()
                                    .setScript(script))));
        }
        plan.setCommands(commands);
        return plan;
    }
}
